Skip to content

ITT-196 review_followup source-of-truth를 .github에 패키징#14

Open
get6 wants to merge 1 commit into
mainfrom
agent/codex/16e2db8b
Open

ITT-196 review_followup source-of-truth를 .github에 패키징#14
get6 wants to merge 1 commit into
mainfrom
agent/codex/16e2db8b

Conversation

@get6
Copy link
Copy Markdown
Member

@get6 get6 commented May 21, 2026

🌷 Summary

  • tools/review_followup/를 Hermes PR review gate tooling의 public source-of-truth로 추가했습니다.
  • source-of-truth와 runtime copy 경계를 문서화하고, template를 ~/.hermes/workspace/tools로 렌더/복사하는 sync_runtime_copy.py를 추가했습니다.
  • 공개 코드에 workspace 고정 UUID나 개인 경로가 남지 않도록 needs-triage 라벨 처리와 runtime path 참조를 정리했습니다.

📢 Description

이번 변경은 review_followup.py, runbook/prompt template, reviewer_roster schema/example, repo-local regression tests를 ittae/.github 안에서 PR 가능한 공용 원본으로 관리하기 위한 패키징입니다.

핵심 변경점:

  • tools/review_followup/README.md에 source-of-truth 정책과 sync 절차를 문서화했습니다.
  • review_followup.py는 public repo에 private workspace label UUID를 고정하지 않도록 라벨명을 기준으로 lookup/create 하게 정리했습니다.
  • multica_agent_guard.py는 홈 디렉터리 절대경로 대신 runtime sibling path를 보도록 정리했습니다.
  • webhook prompt/runbook은 repo 안에서는 template placeholder로 유지하고, install 단계에서 runtime 절대경로로 렌더되게 했습니다.

🐙 Related Issue

Real Behavior Proof (필수)

  • 실제 실행 환경: macOS local workspace + ittae/.github worktree
  • 실행한 명령 / 조작:
    • python3 -m unittest discover -s .github/tools/review_followup/tests -p 'test_*.py'
    • python3 .github/tools/review_followup/sync_runtime_copy.py --runtime-dir "$(mktemp -d)" --dry-run --output json
    • python3 .github/tools/review_followup/sync_runtime_copy.py --runtime-dir "$(mktemp -d)" --output json
  • 결과 증거:
    • unittest Ran 38 tests / OK
    • dry-run에서 planned actions JSON 출력 확인
    • 실제 run에서 rendered prompt/runbook/runtime roster 생성 확인
  • 검증한 시나리오:
    • repo-local regression test 실행
    • template -> runtime copy 렌더링
    • source-of-truth / runtime 경계 문서화
  • 검증하지 않은 영역:
    • 실제 운영 환경 ~/.hermes/workspace/tools에 실적용 후 webhook end-to-end smoke
  • 증거가 부족하다면 그 이유:
    • 이번 PR에서는 public source-of-truth 패키징과 로컬 sync smoke까지만 범위에 포함했습니다.

Human Decision Needed

  • 없음 — 에이전트 판단으로 진행 가능
  • 제품 / UX 결정 필요
  • 비용 / 수익 영향 결정 필요
  • 보안 / 데이터 위험 승인 필요
  • 릴리즈 타이밍 결정 필요

필요하다면 설명:

  • 없음

Copilot AI review requested due to automatic review settings May 21, 2026 08:43
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

이 풀 리퀘스트는 Hermes PR 리뷰 게이트를 위한 공유 도구들을 추가하며, 런타임 복사본 동기화 스크립트, 운영 런북, 웹훅 프롬프트 템플릿 및 리뷰어 로스터 스키마 등을 포함합니다. 리뷰어는 sync_runtime_copy.py에서 runtime_dir을 절대 경로로 변환하여 파일 참조 오류를 방지할 것과, 누락된 multica_agent_guard.py 파일을 복사 대상에 추가할 것을 제안했습니다. 또한, 이식성을 위해 런북과 프롬프트 내의 python3.11 명령어를 일반적인 python3로 변경할 것과 reviewer_roster.schema.json$id 경로 불일치 문제를 해결할 것을 권장했습니다.

def main() -> int:
args = parse_args()
source_dir = Path(__file__).resolve().parent
runtime_dir = Path(args.runtime_dir).expanduser()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

runtime_dir를 절대 경로로 변환하는 것이 안전합니다. 템플릿에 주입된 경로가 상대 경로일 경우, Hermes나 다른 프로세스가 다른 작업 디렉토리에서 실행될 때 파일 참조 오류가 발생할 수 있습니다.

Suggested change
runtime_dir = Path(args.runtime_dir).expanduser()
runtime_dir = Path(args.runtime_dir).expanduser().resolve()

"RUNTIME_TOOLS_DIR": str(runtime_dir),
"REVIEW_FOLLOWUP_PATH": str(runtime_dir / "review_followup.py"),
"REVIEWER_ROSTER_PATH": str(runtime_dir / "reviewer_roster.json"),
"MULTICA_GUARD_PATH": str(runtime_dir / "multica_agent_guard.py"),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

MULTICA_GUARD_PATH가 정의되어 있고 runbook 및 prompt 템플릿에서 사용되고 있지만, multica_agent_guard.py 파일이 DIRECT_COPIES 목록에 누락되어 있습니다. 이 파일이 소스 디렉토리에 존재한다면 복사 대상에 추가해야 하며, 그렇지 않다면 템플릿에서 해당 경로를 사용하는 로직이 의도대로 동작하는지 확인이 필요합니다.

Comment on lines +25 to +28
2. `python3.11 {{REVIEW_FOLLOWUP_PATH}} <ISSUE> --review-pack --post-comment`로 리뷰 요청 pack을 남긴다.
3. Claude/Codex는 high-signal reviewer, Gemini/Copilot은 supplementary reviewer로 리뷰를 수집한다.
4. 리뷰 결과는 `must-fix`, `should-fix`, `question`, `non-actionable` 섹션으로 받는다.
5. `python3.11 {{REVIEW_FOLLOWUP_PATH}} <ISSUE> --post-comment`로 PR 상태, checks, reviewThreads, 리뷰 triage, approval gate를 수집한다.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

특정 Python 버전(python3.11)에 의존하는 대신 일반적인 python3 명령어를 사용하는 것이 이식성 측면에서 좋습니다. review_followup.py의 shebang도 python3를 사용하고 있습니다.

Suggested change
2. `python3.11 {{REVIEW_FOLLOWUP_PATH}} <ISSUE> --review-pack --post-comment` 리뷰 요청 pack을 남긴다.
3. Claude/Codex는 high-signal reviewer, Gemini/Copilot은 supplementary reviewer로 리뷰를 수집한다.
4. 리뷰 결과는 `must-fix`, `should-fix`, `question`, `non-actionable` 섹션으로 받는다.
5. `python3.11 {{REVIEW_FOLLOWUP_PATH}} <ISSUE> --post-comment` PR 상태, checks, reviewThreads, 리뷰 triage, approval gate를 수집한다.
2. python3 {{REVIEW_FOLLOWUP_PATH}} <ISSUE> --review-pack --post-comment로 리뷰 요청 pack을 남긴다.
3. Claude/Codex는 high-signal reviewer, Gemini/Copilot은 supplementary reviewer로 리뷰를 수집한다.
4. 리뷰 결과는 must-fix, should-fix, question, non-actionable 섹션으로 받는다.
5. python3 {{REVIEW_FOLLOWUP_PATH}} <ISSUE> --post-comment로 PR 상태, checks, reviewThreads, 리뷰 triage, approval gate를 수집한다.

Comment on lines +57 to +60
python3.11 {{REVIEW_FOLLOWUP_PATH}} --scan-status in_review --output json
python3.11 {{REVIEW_FOLLOWUP_PATH}} ITT-102 --review-pack --post-comment
python3.11 {{REVIEW_FOLLOWUP_PATH}} ITT-102 --pr-url https://github.com/owner/repo/pull/123 --post-comment
python3.11 {{REVIEW_FOLLOWUP_PATH}} --resolve-pr-url https://github.com/owner/repo/pull/123 --statuses in_review,in_progress,blocked,todo --reviewer-roster-file {{REVIEWER_ROSTER_PATH}} --create-triage-issue-on-miss --output json
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

위와 동일하게 python3.11 대신 python3를 사용하는 것을 권장합니다.

Suggested change
python3.11 {{REVIEW_FOLLOWUP_PATH}} --scan-status in_review --output json
python3.11 {{REVIEW_FOLLOWUP_PATH}} ITT-102 --review-pack --post-comment
python3.11 {{REVIEW_FOLLOWUP_PATH}} ITT-102 --pr-url https://github.com/owner/repo/pull/123 --post-comment
python3.11 {{REVIEW_FOLLOWUP_PATH}} --resolve-pr-url https://github.com/owner/repo/pull/123 --statuses in_review,in_progress,blocked,todo --reviewer-roster-file {{REVIEWER_ROSTER_PATH}} --create-triage-issue-on-miss --output json
python3 {{REVIEW_FOLLOWUP_PATH}} --scan-status in_review --output json
python3 {{REVIEW_FOLLOWUP_PATH}} ITT-102 --review-pack --post-comment
python3 {{REVIEW_FOLLOWUP_PATH}} ITT-102 --pr-url https://github.com/owner/repo/pull/123 --post-comment
python3 {{REVIEW_FOLLOWUP_PATH}} --resolve-pr-url https://github.com/owner/repo/pull/123 --statuses in_review,in_progress,blocked,todo --reviewer-roster-file {{REVIEWER_ROSTER_PATH}} --create-triage-issue-on-miss --output json

Task:
1. Determine the canonical PR URL. Prefer `{pull_request.html_url}`. If that is empty and `{issue.html_url}` is a pull request conversation URL, use that instead.
2. If `{action}` is `closed` and `{pull_request.merged}` resolves to `true`, treat this as merge aftercare instead of review-gate follow-up:
- Run `python3.11 {{REVIEW_FOLLOWUP_PATH}} --merged-aftercare-pr-url <PR_URL> --statuses done,in_review,in_progress,blocked,todo,backlog --reviewer-roster-file {{REVIEWER_ROSTER_PATH}} --create-triage-issue-on-miss --fallback-project-title {{FALLBACK_PROJECT_TITLE}} --event-action "{action}" --sender "{sender.login}" --head-sha "{pull_request.head.sha}" --head-ref "{pull_request.head.ref}" --base-ref "{pull_request.base.ref}" --pr-merged "{pull_request.merged}" --merge-commit-sha "{pull_request.merge_commit_sha}" --merged-at "{pull_request.merged_at}" --merged-by "{pull_request.merged_by.login}" --apply-aftercare --output json`.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

프롬프트 내의 실행 예시에서도 python3.11 대신 python3를 사용하는 것이 범용적입니다.

   - Run python3 {{REVIEW_FOLLOWUP_PATH}} --merged-aftercare-pr-url <PR_URL> --statuses done,in_review,in_progress,blocked,todo,backlog --reviewer-roster-file {{REVIEWER_ROSTER_PATH}} --create-triage-issue-on-miss --fallback-project-title {{FALLBACK_PROJECT_TITLE}} --event-action "{action}" --sender "{sender.login}" --head-sha "{pull_request.head.sha}" --head-ref "{pull_request.head.ref}" --base-ref "{pull_request.base.ref}" --pr-merged "{pull_request.merged}" --merge-commit-sha "{pull_request.merge_commit_sha}" --merged-at "{pull_request.merged_at}" --merged-by "{pull_request.merged_by.login}" --apply-aftercare --output json.

- If `aftercare.state` is `blocked`, return `blocked` with the ambiguous families or warnings instead of guessing.
- Do not run the review-gate flow below for merged PRs.
3. If this is an `issue_comment` payload without a pull request URL, stop and return `blocked` with the reason `non-pr-issue-comment`.
4. Run `python3.11 {{REVIEW_FOLLOWUP_PATH}} --resolve-pr-url <PR_URL> --statuses in_review,in_progress,blocked,todo --reviewer-roster-file {{REVIEWER_ROSTER_PATH}} --create-triage-issue-on-miss --fallback-project-title {{FALLBACK_PROJECT_TITLE}} --event-name "{event}" --event-action "{action}" --sender "{sender.login}" --review-author "{review.user.login}" --review-id "{review.id}" --review-state "{review.state}" --review-body "{review.body}" --review-url "{review.html_url}" --comment-author "{comment.user.login}" --comment-id "{comment.id}" --comment-body "{comment.body}" --comment-url "{comment.html_url}" --head-sha "{pull_request.head.sha}" --output json`.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

python3.11 대신 python3를 사용하도록 수정합니다.

4. Run python3 {{REVIEW_FOLLOWUP_PATH}} --resolve-pr-url <PR_URL> --statuses in_review,in_progress,blocked,todo --reviewer-roster-file {{REVIEWER_ROSTER_PATH}} --create-triage-issue-on-miss --fallback-project-title {{FALLBACK_PROJECT_TITLE}} --event-name "{event}" --event-action "{action}" --sender "{sender.login}" --review-author "{review.user.login}" --review-id "{review.id}" --review-state "{review.state}" --review-body "{review.body}" --review-url "{review.html_url}" --comment-author "{comment.user.login}" --comment-id "{comment.id}" --comment-body "{comment.body}" --comment-url "{comment.html_url}" --head-sha "{pull_request.head.sha}" --output json.

6. If `resolution.state == needs-triage` and `created_issue` exists, stop there. The resolver already left a visible Multica tracking issue, so do not post an extra comment elsewhere.
7. If `resolution.state == ignored-external-repo`, stop there and return `external-repo-skipped`. Unlinked fallback tracking applies only to `owner == ittae` repositories.
8. If more than one linked Multica issue matches the same PR, return `blocked` and list the candidates instead of guessing.
9. If exactly one linked issue exists, run `python3.11 {{REVIEW_FOLLOWUP_PATH}} <ISSUE_IDENTIFIER> --pr-url <PR_URL> --reviewer-roster-file {{REVIEWER_ROSTER_PATH}} --event-name "{event}" --event-action "{action}" --sender "{sender.login}" --review-author "{review.user.login}" --review-id "{review.id}" --review-state "{review.state}" --review-body "{review.body}" --review-url "{review.html_url}" --comment-author "{comment.user.login}" --comment-id "{comment.id}" --comment-body "{comment.body}" --comment-url "{comment.html_url}" --head-sha "{pull_request.head.sha}" --stabilize-seconds 5 --stabilize-attempts 2 --output json`.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

python3.11 대신 python3를 사용하도록 수정합니다.

9. If exactly one linked issue exists, run python3 {{REVIEW_FOLLOWUP_PATH}} <ISSUE_IDENTIFIER> --pr-url <PR_URL> --reviewer-roster-file {{REVIEWER_ROSTER_PATH}} --event-name "{event}" --event-action "{action}" --sender "{sender.login}" --review-author "{review.user.login}" --review-id "{review.id}" --review-state "{review.state}" --review-body "{review.body}" --review-url "{review.html_url}" --comment-author "{comment.user.login}" --comment-id "{comment.id}" --comment-body "{comment.body}" --comment-url "{comment.html_url}" --head-sha "{pull_request.head.sha}" --stabilize-seconds 5 --stabilize-attempts 2 --output json.

@@ -0,0 +1,74 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/ittae/.github/tools/review_followup/reviewer_roster.schema.json",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

$id의 경로(https://github.com/ittae/.github/tools/...)와 실제 파일 경로(tools/...)가 일치하지 않습니다. 또한 PR 설명의 검증 명령어들이 .github/tools/ 경로를 사용하고 있는 것으로 보아, 파일들이 .github/ 디렉토리 아래로 이동되어야 하는지 확인이 필요합니다.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Hermes의 PR 리뷰 게이트 도구(review_followup)를 tools/review_followup/ 아래 “공개 source-of-truth”로 정리하고, 런타임 디렉터리로 동기화하는 설치 스크립트/템플릿 문서/roster 스키마 및 회귀 테스트를 추가한 PR입니다.

Changes:

  • tools/review_followup/에 runbook/webhook prompt/roster schema+example/runtime sync 스크립트/테스트를 추가
  • GitHub webhook 기반 follow-up/merged aftercare 플로우를 문서화하고(템플릿 치환 포함) 런타임 복사본 생성 경계를 명확화
  • repo-local unittest 기반 회귀 테스트 2종 추가 및 루트 README에 tooling 링크 추가

Reviewed changes

Copilot reviewed 9 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tools/review_followup/tests/test_review_followup.py follow-up policy/notification 로직 회귀 테스트 추가
tools/review_followup/tests/test_review_followup_runtime.py 문서/CLI 엔트리포인트/merged-aftercare 경로 포함 런타임 스모크 테스트 추가
tools/review_followup/sync_runtime_copy.py source-of-truth → 런타임 디렉터리 동기화(템플릿 렌더링 포함) 스크립트 추가
tools/review_followup/reviewer_roster.schema.json reviewer roster JSON schema 추가
tools/review_followup/reviewer_roster.example.json 런타임 bootstrap 용 example roster 추가
tools/review_followup/review_followup_webhook_prompt.txt webhook prompt 템플릿 추가(merge aftercare + follow-up 분기 포함)
tools/review_followup/review_followup_runbook.md 운영 runbook 템플릿 추가(경계/게이트/알림/aftercare 정책 포함)
tools/review_followup/README.md 디렉터리 경계/컨텐츠/동기화/테스트 방법 문서 추가
README.md shared tooling 링크 추가
.gitignore Python 캐시/바이트코드 무시 규칙 추가

Comment on lines +21 to +52
"role": {
"type": "string",
"enum": ["required", "supplementary"]
},
"availability": {
"type": "string",
"enum": ["active", "inactive"]
},
"legacy_names": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
},
"signal_source": {
"oneOf": [
{
"type": "string",
"enum": ["multica", "github-review"]
},
{
"type": "array",
"minItems": 1,
"uniqueItems": true,
"items": {
"type": "string",
"enum": ["multica", "github-review"]
}
}
]
},
Comment on lines +106 to +110
def write_roster(payload: dict) -> str:
handle = tempfile.NamedTemporaryFile("w", suffix=".json", delete=False)
with handle:
json.dump(payload, handle)
return handle.name
Comment on lines +63 to +64
runbook = RUNBOOK_PATH.read_text()
prompt = PROMPT_PATH.read_text()
@get6 get6 changed the title ITT-196 package review_followup source-of-truth in .github ITT-196 review_followup source-of-truth를 .github에 패키징 May 21, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d6c9d69974

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +324 to +326
@patch.object(review_followup, "gh_review_threads", return_value=([], None))
@patch.object(review_followup, "gh_pr_checks", return_value=([{"name": "CI", "state": "SUCCESS"}], None))
@patch.object(review_followup, "gh_repo_default_branch", return_value=("main", None))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Mock all GitHub CLI calls in this unit test

test_main_accepts_pr_423_claude_github_signal patches gh_pr_view, gh_pr_checks, and gh_review_threads, but main() now also reaches gh_pr_reviews, gh_issue_comments, and gh_commit_details via collect_issue_follow_up_context. In environments without gh installed or authenticated, this test fails with a subprocess error and becomes nondeterministic instead of hermetic. Please patch the remaining GitHub-call helpers (or inject a fake command runner) so the test does not depend on external CLI/network state.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants